/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.ibatis.scripting.xmltags;

import com.je.ibatis.extension.conditions.ConditionsWrapper;
import com.je.ibatis.extension.enums.Conditions;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.builder.SqlSourceBuilder;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.scripting.xmltags.DynamicContext;
import org.apache.ibatis.scripting.xmltags.DynamicSqlSource;
import org.apache.ibatis.scripting.xmltags.SqlNode;
import org.apache.ibatis.session.Configuration;

import static com.je.ibatis.extension.toolkit.Constants.WRAPPER_ALIAS;
import static com.je.ibatis.extension.toolkit.Constants.WRAPPER_PARAMETER_PREFIX;

/**
 * 动态数据源,支持条件解析器
 *
 * @author wangmm@ketr.com.cn
 * @date 2019/11/27
 */
public class CustomDynamicSqlSource extends DynamicSqlSource {

    private static final Log LOGGER = LogFactory.getLog(CustomDynamicSqlSource.class);
    private final Configuration configuration;
    private final SqlNode rootSqlNode;
    private final boolean sqlParamLog;

    public CustomDynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
        super(configuration, rootSqlNode);
        this.configuration = configuration;
        this.rootSqlNode = rootSqlNode;
        sqlParamLog = "true".equalsIgnoreCase(configuration.getVariables().getProperty("sqlParamLog"));
    }

    @Override
    public BoundSql getBoundSql(Object parameterObject) {
        DynamicContext context = new DynamicContext(configuration, parameterObject);
        rootSqlNode.apply(context);

        //包装方法参数
        final MetaObject metaParam = configuration.newMetaObject(context.getBindings().get(DynamicContext.PARAMETER_OBJECT_KEY));
        // 校验是否包含条件解析器
        if (metaParam.hasGetter(WRAPPER_ALIAS) && metaParam.getValue(WRAPPER_ALIAS) != null) {
            //获取条件解析器
            ConditionsWrapper wrapper = (ConditionsWrapper) metaParam.getValue(WRAPPER_ALIAS);
            //如果不为空的条件语句，并且不以where或order by开头，拼接where
            if (!wrapper.isDDL() && wrapper.isCondition() && !wrapper.isEmpty() && !wrapper.hasWhere()
                    && !wrapper.getSql().trim().toUpperCase().startsWith(Conditions.ORDER_BY.getSqlSegment())) {
                context.appendSql(Conditions.WHERE.getSqlSegment());
            }
            //拼接条件解析器Sql
            wrapper.getSqlNode().apply(context);

            //日志
            if (sqlParamLog) {
                //参数打印
                if (!wrapper.getParameter().isEmpty()) {
                    LOGGER.info(wrapper.getParameter());
                }
            }
        }
        SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
        Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
        //将#{w.map.var}变量转换为预处理符号(?)
        SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
        BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
        context.getBindings().forEach(boundSql::setAdditionalParameter);
        return boundSql;
    }

}
